
/**
 * AppleIIGo
 * SmartPort Unit class
 * (C) 2008 by Marc S. Ressl (mressl@umich.edu)
 * Released under the GPL
 */

#include <string.h>
#include "SmartPortUnit.h"
#include "GCRDisk35DiskImage.h"

SmartPortUnit::SmartPortUnit(UINT32 diskDriveType) {
	diskDrivePort = DISKDRIVEPORT_SMARTPORT;
	this->diskDriveType = diskDriveType;
	
	deviceId = "SMARTPORT UNIT";
	deviceType = 0x02;
	deviceSubtype = 0x40;
	deviceVersion = 0x0;
	
	mount(0);
}

SmartPortUnit::~SmartPortUnit() {
	unmount();
}

bool SmartPortUnit::mount(char * diskImagePath) {
	FileDiskImage * diskImage;
	UINT32 diskType;
	if (diskDriveType == DISKDRIVETYPE_3_5)
		diskType = FILEDISKTYPE_3_5;
	else
		diskType = FILEDISKTYPE_HDD;
	diskImage = FileDiskImage::getInstance(diskType, diskImagePath);
	
	if (!diskImage)
		return false;
	
	if (this->diskImage)
		delete this->diskImage;
	this->diskImage = diskImage;
	
	return true;
}

void SmartPortUnit::unmount() {
	mount(0);
}

char * SmartPortUnit::getDiskImagePath() {
	return diskImage->getDiskImagePath();
}

void SmartPortUnit::getStatus(SmartPortPacket * packet) {
	UINT8 parameterNum = packet->data[1];
	
	if (parameterNum != 3) {
		packet->status = SMARTPORTERROR_BADPCNT;
		return;
	}
	
	UINT32 statCode = packet->data[4];
	switch (statCode) {
		case 0:
		case 3: {
			UINT32 blockNum = diskImage->getBlockNum();
			packet->data[0] = ((diskImage->isWriteProtected() ?
								SMARTPORTSTATUS_WRITEPROTECTED : 0) |
							   SMARTPORTSTATUS_FORMATENABLED |
							   (diskImage->isOnline() ?
								SMARTPORTSTATUS_ONLINE : 0) |
							   SMARTPORTSTATUS_READENABLED |
							   SMARTPORTSTATUS_WRITEENABLED |
							   SMARTPORTSTATUS_BLOCKDEVICE);
			packet->data[1] = (blockNum >> 0) & 0xff;
			packet->data[2] = (blockNum >> 8) & 0xff;
			packet->data[3] = (blockNum >> 16) & 0xff;
			if (statCode == 0)
				packet->dataByteNum = 4;
			else {
				printf("SmartPort Exception Report: status 0x03 request, "
					   "device: %02x\n", packet->sourceId);
				packet->data[4] = strlen(deviceId);
				memset(&packet->data[5], ' ', 16);
				memcpy(&packet->data[5], deviceId, strlen(deviceId));
				packet->data[21] = deviceType;
				packet->data[22] = deviceSubtype;
				packet->data[23] = (deviceVersion >> 0) & 0xff;
				packet->data[24] = (deviceVersion >> 8) & 0xff;
				packet->dataByteNum = 25;
			}
			break;
		}
		case 1:
			printf("SmartPort Exception Report: status 0x01 request, "
				   "device: %02x\n", packet->sourceId);
			packet->data[0] = 1;
			packet->data[1] = 0;
			packet->dataByteNum = 2;
			break;
		default:
			printf("SmartPort Exception Report: bad status %02x request, "
				   "device: %02x\n", statCode, packet->sourceId);
			packet->status = SMARTPORTERROR_BADCTL;
			break;
	}
	
	return;
}

UINT32 SmartPortUnit::accessBlock(SmartPortPacket * packet, bool isWrite) {
	UINT8 parameterNum = packet->data[1];
	
	if (parameterNum != 3) {
		packet->status = SMARTPORTERROR_BADPCNT;
		return SMARTPORTACTION_REPLYPACKET;
	}
	
	if (!diskImage->isOnline()) {
		packet->status = SMARTPORTERROR_OFFLINE;
		return SMARTPORTACTION_REPLYPACKET;
	}
	
	UINT32 blockIndex = (packet->data[4] |
						 (packet->data[5] << 8) |
						 (packet->data[6] << 16));
	if (!diskImage->seekBlock(blockIndex)) {
		packet->status = SMARTPORTERROR_BADBLOCK;
		return SMARTPORTACTION_REPLYPACKET;
	}
	
	if (isWrite)
		return SMARTPORTACTION_REPLYACK;
	else {
		if (!diskImage->readBlock(packet->data))
			packet->status = SMARTPORTERROR_IOERROR;
		else {
			packet->type = SMARTPORTPACKETTYPE_DATA;
			packet->dataByteNum = PRODOS_BLOCKBYTENUM;
		}
	}
	
	return SMARTPORTACTION_REPLYPACKET;
}

void SmartPortUnit::formatDevice(SmartPortPacket * packet) {
	if (!diskImage->isOnline())
		packet->status = SMARTPORTERROR_OFFLINE;
	else if (diskImage->isWriteProtected())
		packet->status = SMARTPORTERROR_NOWRITE;
}

void SmartPortUnit::sendControl(SmartPortPacket * packet) {
	UINT8 parameterNum = packet->data[1];
	
	if (parameterNum != 3) {
		packet->status = SMARTPORTERROR_BADPCNT;
		return;
	}
	
	UINT8 controlCode = packet->data[4];
	switch (controlCode) {
		case 0:
			break;
		case 4:
			printf("SmartPort Exception Report: eject request, "
				   "device: %02x\n", packet->sourceId);
			unmount();
			break;
		default:
			printf("SmartPort Exception Report: bad ctl request, "
				   "device: %02x\n", controlCode, packet->sourceId);
			packet->status = SMARTPORTERROR_BADCTL;
			break;
	}
}

UINT32 SmartPortUnit::sendPacket(SmartPortPacket * packet) {
	UINT8 packetType = packet->type;
	UINT32 dataByteNum = packet->dataByteNum;
	
	packet->sourceId = packet->destinationId;
	packet->destinationId = 0;
	packet->type = SMARTPORTPACKETTYPE_STATUS;
	packet->auxType = 0;
	packet->status = SMARTPORTERROR_NOERROR;
	packet->dataByteNum = 0;
	
	switch (packetType) {
		case SMARTPORTPACKETTYPE_COMMAND:
			if (dataByteNum != 9)
				return SMARTPORTACTION_NOREPLY;
			
			UINT8 command = packet->data[0];
			switch (command) {
				case SMARTPORTCOMMAND_STATUS:
					getStatus(packet);
					break;
				case SMARTPORTCOMMAND_READBLOCK:
					return accessBlock(packet, false);
				case SMARTPORTCOMMAND_WRITEBLOCK:
					return accessBlock(packet, true);
				case SMARTPORTCOMMAND_FORMAT:
					printf("SmartPort Exception Report: FORMAT command, "
						   "device: %02x\n", packet->sourceId);
					formatDevice(packet);
					break;
				case SMARTPORTCOMMAND_CONTROL:
					printf("SmartPort Exception Report: CONTROL command, "
						   "device: %02x\n", packet->sourceId);
					sendControl(packet);
					break;
				default:
					printf("SmartPort Exception Report: bad command %02x, "
						   "device: %02x\n", command, packet->sourceId);
					packet->status = SMARTPORTERROR_BADCMD;
					break;
			}
			break;
		case SMARTPORTPACKETTYPE_DATA:
			if (dataByteNum != 512)
				return SMARTPORTACTION_NOREPLY;
			else if (!diskImage->isOnline())
				packet->status = SMARTPORTERROR_OFFLINE;
			else if (diskImage->isWriteProtected())
				packet->status = SMARTPORTERROR_NOWRITE;
			else if (!diskImage->writeBlock(packet->data))
				packet->status = SMARTPORTERROR_IOERROR;
			
			break;
	}
	
	return SMARTPORTACTION_REPLYPACKET;
}
